package models;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import models.Evaluator.Evaluation;
import models.Evaluator.Winner;
import models.StateManager.GameState;
import models.exceptions.CreatingRoomException;
import models.exceptions.DeckException;
import models.exceptions.EvaluatorException;
import models.exceptions.JsonOutputException;
import models.exceptions.LicitatingException;
import models.exceptions.PublicException;
import models.exceptions.RoomJoinException;
import models.messages.AreYouHuman;
import models.messages.CloseConnection;
import models.messages.CloseRoom;
import models.messages.ConsoleMsg;
import models.messages.EvaluationsNames;
import models.messages.ExchangedCards;
import models.messages.ExchangingCards;
import models.messages.ItsYourTurn;
import models.messages.Licitating;
import models.messages.NewOpponent;
import models.messages.NewResults;
import models.messages.OpponentCards;
import models.messages.OpponentTurn;
import models.messages.OtherOpponents;
import models.messages.Quit;
import models.messages.RemoveOpponent;
import models.messages.RevealCards;
import models.messages.RoomJoin;
import models.messages.TellYourCards;
import models.messages.TellYourName;
import models.messages.YourCards;
import models.players.Human;
import models.players.RandomBot;
import play.Logger;
import play.libs.*;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.*;
import static akka.pattern.Patterns.ask;
import static java.util.concurrent.TimeUnit.*;

import com.fasterxml.jackson.databind.node.ObjectNode;

public class GameRoom extends UntypedActor
{
    Map<String, ActorRef> players     = new HashMap<String, ActorRef>();
    List<String>          names       = new LinkedList<String>();
    Map<String, String>   uuids       = new HashMap<String, String>();
    Map<String, Integer>  results     = new HashMap<String, Integer>();
    int                   maxPlayers  = 4;
    int                   humans      = 0;
    String                begginer    = "";
    boolean               gameStarted = false;
    boolean               endOfMatch  = false;
    Deck                  deck;
    static String[]       botNames    = { "Amelia", "Rose", "Jackie", "Rory",
            "Pete", "Sally", "Martha", "Lance", "Luke", "Jack", "Gwen",
            "Ricky", "Mickey", "Jenny", "Wilfred", "Idris", "Charles" };
    Random                rand;
    StateManager          state;
    TurnManager           turn;
    BetsManager           bets;
    int                   initCash;
    int                   initFee;

    public GameRoom(int initCash, int initFee)
    {
        this.deck = new Deck();
        this.rand = new Random();
        this.state = new StateManager();
        this.turn = new TurnManager();
        this.bets = new BetsManager();
        this.initCash = initCash;
        this.initFee = initFee;
        Logger.debug("[  GameRoom   ] Start cash: " + initCash
                + ", Enter fee: " + initFee);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void onReceive(Object msg) throws Exception
    {
        try
        {
            if (msg instanceof RoomJoin)
            {
                RoomJoin roomJoin = (RoomJoin) msg;
                Logger.info("[  GameRoom   ] Joined room (" + roomJoin.uuid
                        + ")");
                if (!this.state.canJoin())
                {
                    throw new RoomJoinException(
                            "The game has already started.", roomJoin.out, true);
                }
                if (this.freeSits() <= 0)
                {
                    throw new RoomJoinException(
                            "Maximum number of players exceeded.",
                            roomJoin.out, true);
                }
                if ((this.players.size() == 0)
                        && !roomJoin.futureRoom.isTemplate())
                {
                    throw new RoomJoinException(
                            "Room you wanted to join has been dismissed.",
                            roomJoin.out, true);
                }
                else if ((this.players.size() == 0)
                        && roomJoin.futureRoom.isTemplate())
                {
                    this.maxPlayers = 1;
                    ActorRef joinMe = Akka.system().actorOf(
                            Props.create(Human.class, roomJoin.uuid,
                                    roomJoin.futureRoom.ownerName, roomJoin.in,
                                    roomJoin.out, this.getSelf()));
                    this.players.put(roomJoin.uuid, joinMe);
                    this.names.add(roomJoin.futureRoom.ownerName);
                    this.uuids
                            .put(roomJoin.uuid, roomJoin.futureRoom.ownerName);
                    this.results.put(roomJoin.uuid, 0);
                    this.turn.addPlayer(roomJoin.uuid);
                    this.bets.addPlayer(roomJoin.uuid, this.initCash);
                    this.humans++;
                    Logger.debug("[  GameRoom   ] Human actor created.");
                    for (String t : roomJoin.futureRoom.opponents)
                    {
                        switch (t)
                        {
                            case "0":
                                // nothing - w8ing for human to connect
                                Logger.debug("[  GameRoom   ] Making place for human.");
                                this.maxPlayers++;
                                break;
                            case "1":
                                // new random bot
                                Logger.debug("[  GameRoom   ] Spawning bot.");
                                String randomUUID = java.util.UUID.randomUUID()
                                        .toString();
                                String name = this.getRandomName();
                                ActorRef bot = Akka.system().actorOf(
                                        Props.create(RandomBot.class,
                                                randomUUID, name,
                                                this.getSelf()));
                                this.players.put(randomUUID, bot);
                                this.names.add(name);
                                this.uuids.put(randomUUID, name);
                                this.results.put(randomUUID, 0);
                                this.turn.addPlayer(randomUUID);
                                this.bets.addPlayer(randomUUID, this.initCash);
                                this.maxPlayers++;
                                joinMe.tell(new NewOpponent(name),
                                        this.getSelf());
                                break;
                            default:
                                throw new CreatingRoomException(
                                        "Wrong opponent type.", roomJoin.out,
                                        true);
                        }
                    }
                }
                else
                {
                    if (this.names.contains(roomJoin.futureRoom.ownerName))
                    {
                        throw new RoomJoinException(
                                "This nickname is already taken.",
                                roomJoin.out, true);
                    }
                    Logger.info("[  GameRoom   ] Entering existing room.");
                    ActorRef joinMe = Akka.system().actorOf(
                            Props.create(Human.class, roomJoin.uuid,
                                    roomJoin.futureRoom.ownerName, roomJoin.in,
                                    roomJoin.out, this.getSelf()));
                    joinMe.tell(new OtherOpponents(new LinkedList<String>(
                            this.names)), this.getSelf());
                    this.tellAll(new NewOpponent(roomJoin.futureRoom.ownerName));
                    this.players.put(roomJoin.uuid, joinMe);
                    this.names.add(roomJoin.futureRoom.ownerName);
                    this.uuids
                            .put(roomJoin.uuid, roomJoin.futureRoom.ownerName);
                    this.results.put(roomJoin.uuid, 0);
                    this.turn.addPlayer(roomJoin.uuid);
                    this.bets.addPlayer(roomJoin.uuid, this.initCash);
                    this.humans++;
                }
                this.consoleAll("success", roomJoin.futureRoom.ownerName
                        + " joined the game.");
                if (this.maxPlayers == this.players.size())
                {
                    Logger.info("[  GameRoom   ] Starting game.");
                    this.consoleAll("success", "Starting the game.");
                    this.turn.shuffle();
                    this.turn.nextTurn();
                    this.state.nextState();
                    this.takeFee();
                    this.dealCards();
                    this.state.nextState();
                    this.tellTurn();
                }
                else
                {
                    this.consoleAll("normal",
                            "Waiting for other players to join.");
                }
                Logger.debug("[  GameRoom   ] Players: "
                        + this.names.toString() + ".");
            }
            else if (msg instanceof ExchangingCards)
            {
                ExchangingCards ec = (ExchangingCards) msg;
                Logger.info("[  GameRoom   ] Player is requesting cards exchange.");
                try
                {
                    if (!this.state.canExchange())
                    {
                        throw new PublicException(
                                "It's not the time to exchange cards.");
                    }
                    if (!this.turn.myTurn(ec.uuid))
                    {
                        throw new DeckException(
                                "It's not your turn to exchange cards.");
                    }
                    if (ec.cards.size() > 0)
                    {
                        this.deck.putCards(ec.cards);
                        List<Card> l = this.deck.giveCards(ec.cards.size());
                        this.players.get(ec.uuid)
                                .tell(new ExchangedCards(l, ec.cards),
                                        this.getSelf());
                    }

                    if (this.turn.atEnd())
                    {
                        this.state.nextState();
                        this.turn.nextTurn();
                        this.tellTurn();
                    }
                    else
                    {
                        this.turn.nextTurn();
                        this.tellTurn();
                    }
                }
                catch (DeckException e)
                {
                    Logger.error(e.getMessage());
                    this.players.get(ec.uuid).tell(
                            new ConsoleMsg("danger", e.getMessage()),
                            this.getSelf());
                }
                catch (EvaluatorException e)
                {
                    Logger.error(e.getMessage());
                    this.tellAll(new ConsoleMsg("danger", e.getMessage()));
                }
                catch (PublicException e)
                {
                    Logger.error(e.getMessage());
                    this.players.get(ec.uuid).tell(
                            new ConsoleMsg("danger", e.getMessage()),
                            this.getSelf());
                }
            }
            else if (msg instanceof Licitating)
            {
                Licitating licitating = (Licitating) msg;
                String name = this.uuids.get(licitating.uuid);
                Logger.info("[  GameRoom   ] Player is licitating.");
                try
                {

                    if (!this.state.canLicitate())
                    {
                        throw new PublicException(
                                "It's not the time to licitate.");
                    }
                    if (!this.turn.myTurn(licitating.uuid))
                    {
                        throw new PublicException(
                                "It's not your turn for licitating.");
                    }
                    switch (licitating.operation)
                    {
                        case "check":
                            if (!this.bets.canCheck())
                            {
                                throw new LicitatingException(
                                        "You can't check now.");
                            }
                            this.bets.check(licitating.uuid);
                            this.consoleAll("normal", name + " checks.");
                            break;
                        case "bet":
                            if (!this.bets.canBet(licitating.uuid,
                                    licitating.value))
                            {
                                throw new LicitatingException("You can't bet "
                                        + licitating.value + " now.");
                            }
                            this.bets.bet(licitating.uuid, licitating.value);
                            this.consoleAll("normal", name + " bets "
                                    + licitating.value + ".");
                            break;
                        case "fold":
                            this.bets.fold(licitating.uuid);
                            this.turn.addFolder(licitating.uuid);
                            this.consoleAll("normal", name + " folds.");
                            break;
                        case "call":
                            if (!this.bets.canCall(licitating.uuid))
                            {
                                throw new LicitatingException(
                                        "You can't call now.");
                            }
                            this.bets.call(licitating.uuid);
                            this.consoleAll("normal", name + " calls.");
                            break;
                        case "raise":
                            if (!this.bets.canRaise(licitating.uuid,
                                    licitating.value))
                            {
                                throw new LicitatingException(
                                        "You can't raise by "
                                                + licitating.value + " now.");
                            }
                            this.bets.raise(licitating.uuid, licitating.value);
                            this.consoleAll("normal", name + " raises by "
                                    + licitating.value + ".");
                            break;
                        case "allin":
                            if (!this.bets.canAllIn(licitating.uuid))
                            {
                                throw new LicitatingException(
                                        "You can't play all in now.");
                            }
                            this.bets.allIn(licitating.uuid);
                            this.consoleAll("normal", name + " plays all in.");
                            break;
                        default:
                            throw new LicitatingException("Unkown operand `"
                                    + licitating.operation + "`.");
                    }
                    this.tellAll(new NewResults(name, this.bets
                            .getMoney(licitating.uuid), this.results
                            .get(licitating.uuid)));
                    if (this.turn.atEnd() || this.turn.onePlayerLeft())
                    {
                        System.out.println("atEnd ? " + this.turn.atEnd());
                        if (this.bets.auctionCanEnd()
                                || this.turn.onePlayerLeft())
                        {
                            System.out.println("auctionEnd ? "
                                    + this.bets.auctionCanEnd());
                            this.state.nextState();
                            if (this.state.current().equals(
                                    StateManager.GameState.END)
                                    || this.turn.onePlayerLeft())
                            {
                                this.revealCards();
                                this.evaluateHands();
                                Akka.system()
                                        .scheduler()
                                        .scheduleOnce(
                                                Duration.create(10, SECONDS),
                                                new Runnable()
                                                {
                                                    private GameRoom gr;

                                                    public Runnable init(
                                                            GameRoom gr)
                                                    {
                                                        this.gr = gr;
                                                        return this;
                                                    }

                                                    @Override
                                                    public void run()
                                                    {
                                                        try
                                                        {
                                                            if (this.gr.players
                                                                    .size() > 1)
                                                            {
                                                                this.gr.turn
                                                                        .clearFolders();
                                                                this.gr.turn
                                                                        .shift();
                                                                this.gr.turn
                                                                        .nextTurn();
                                                                if (!this.gr.state
                                                                        .current()
                                                                        .equals(StateManager.GameState.END))
                                                                {
                                                                    this.gr.state.state = GameState.BETS;
                                                                }
                                                                else
                                                                {
                                                                    this.gr.state
                                                                            .nextState();
                                                                }
                                                                this.gr.takeFee();
                                                                this.gr.dealCards();
                                                                this.gr.state
                                                                        .nextState();
                                                                this.gr.tellTurn();
                                                            }
                                                        }
                                                        catch (Exception e)
                                                        {
                                                            // TODO
                                                            // Auto-generated
                                                            // catch
                                                            // block
                                                            e.printStackTrace();
                                                        }
                                                    }
                                                }.init(this),
                                                Akka.system().dispatcher());
                            }
                            else
                            {
                                this.turn.nextTurn();
                                this.tellTurn();
                            }
                        }
                        else
                        {
                            this.turn.nextTurn();
                            this.tellTurn();
                        }
                    }
                    else
                    {
                        this.turn.nextTurn();
                        this.tellTurn();
                    }
                }
                catch (LicitatingException e)
                {
                    Logger.error("[  GameRoom   ] " + e.getMessage());
                    ActorRef hisTurn = this.players.get(licitating.uuid);
                    hisTurn.tell(new ConsoleMsg("danger", e.getMessage()),
                            this.getSelf());
                    hisTurn.tell(
                            new ItsYourTurn(this.state.current(), this.bets
                                    .canCheck(), this.bets.canBet(), this.bets
                                    .canRaise(this.turn.current()), this.bets
                                    .canCall(this.turn.current()), this.bets
                                    .canAllIn(this.turn.current()),
                                    this.bets.maxBet, this.bets
                                            .getMoney(this.turn.current()),
                                    this.bets.getBet(this.turn.current()),
                                    this.bets.getPool()), this.getSelf());
                }
                catch (PublicException e)
                {
                    Logger.error("[  GameRoom   ] " + e.getMessage());
                    this.players.get(licitating.uuid).tell(
                            new ConsoleMsg("danger", e.getMessage()),
                            this.getSelf());
                }
            }
            else if (msg instanceof Quit)
            {
                Quit quit = (Quit) msg;
                ActorRef player = this.players.get(quit.uuid);
                if ((player != null))
                {
                    List<Card> hand = (List<Card>) Await.result(
                            ask(player, new TellYourCards(), 1000),
                            Duration.create(5, SECONDS));
                    if (hand.size() > 0)
                    {
                        this.deck.putCards(hand);
                    }
                    String name = (String) Await.result(
                            ask(player, new TellYourName(), 1000),
                            Duration.create(1, SECONDS));
                    boolean isHuman = (Boolean) Await.result(
                            ask(player, new AreYouHuman(), 1000),
                            Duration.create(1, SECONDS));
                    this.consoleAll("danger", name + " has left the game.");
                    player.tell(akka.actor.PoisonPill.getInstance(),
                            this.getSelf());
                    Logger.info("[  GameRoom   ] " + name
                            + " has left the game.");

                    if (isHuman)
                    {
                        this.humans--;
                    }
                    if (!this.hasHumans() || (this.players.size() < 3))
                    {
                        Logger.debug("[  GameRoom   ] Closing connections.");
                        for (Map.Entry<String, ActorRef> entry : this.players
                                .entrySet())
                        {
                            ActorRef p = entry.getValue();
                            p.tell(new CloseConnection(), this.getSelf());
                            p.tell(akka.actor.PoisonPill.getInstance(),
                                    this.getSelf());
                        }
                        this.players.clear();
                        this.names.clear();
                        this.uuids.clear();
                        this.results.clear();
                        GameManager.self.tell(new CloseRoom(), this.getSelf());
                    }
                    else
                    {
                        if (this.state.canJoin())
                        {
                            this.players.remove(quit.uuid);
                            this.names.remove(name);
                            this.uuids.remove(quit.uuid);
                            this.results.remove(quit.uuid);
                            this.turn.rmPlayer(quit.uuid);
                            this.bets.rmPlayer(quit.uuid);

                            this.tellAll(new RemoveOpponent(name));
                        }
                        else
                        {
                            String nextTurn;
                            ActorRef hisTurn;

                            if (this.turn.myTurn(quit.uuid))
                            {
                                this.turn.rmPlayer(quit.uuid);
                                this.bets.rmPlayer(quit.uuid);
                                nextTurn = this.turn.nextTurn();
                                hisTurn = this.players.get(nextTurn);

                                this.players.remove(quit.uuid);
                                this.names.remove(name);
                                this.uuids.remove(quit.uuid);
                                this.results.remove(quit.uuid);

                                this.tellAll(new RemoveOpponent(name));
                            }
                            else
                            {
                                this.turn.rmPlayer(quit.uuid);
                                this.bets.rmPlayer(quit.uuid);
                                this.players.remove(quit.uuid);
                                this.names.remove(name);
                                this.uuids.remove(quit.uuid);
                                this.results.remove(quit.uuid);

                                this.tellAll(new RemoveOpponent(name));

                                nextTurn = this.turn.nextTurn();
                                hisTurn = this.players.get(nextTurn);
                            }
                            this.tellAll(new OpponentTurn(this.uuids
                                    .get(nextTurn)));
                            hisTurn.tell(
                                    new ItsYourTurn(this.state.current(),
                                            this.bets.canCheck(), this.bets
                                                    .canBet(), this.bets
                                                    .canRaise(this.turn
                                                            .current()),
                                            this.bets.canCall(this.turn
                                                    .current()), this.bets
                                                    .canAllIn(this.turn
                                                            .current()),
                                            this.bets.maxBet, this.bets
                                                    .getMoney(this.turn
                                                            .current()),
                                            this.bets.getBet(this.turn
                                                    .current()), this.bets
                                                    .getPool()), this.getSelf());
                        }
                    }
                }
            }
            else
            {
                this.unhandled(msg);
            }
        }
        catch (JsonOutputException e)
        {
            Logger.error("[  GameRoom   ] " + e.getMessage());
            ObjectNode event = Json.newObject();
            event.put("type", "consoleMsg");
            event.put("msgType", "danger");
            event.put("msg", e.getMessage());
            e.out.write(event);
            if (e.closeSocket)
            {
                e.out.close();
            }
            if (e instanceof CreatingRoomException)
            {
                GameManager.self.tell(new CloseRoom(), this.getSelf());
            }
        }
        catch (Exception e)
        {
            Logger.error("[  GameRoom   ] " + e.getMessage());
            throw e;
        }

    }

    public void consoleAll(String kind, String text)
    {
        for (ActorRef player : this.players.values())
        {
            player.tell(new ConsoleMsg(kind, text), this.getSelf());
        }
    }

    public void tellAll(Object msg)
    {
        for (ActorRef player : this.players.values())
        {
            player.tell(msg, this.getSelf());
        }
    }

    public boolean hasHumans() throws Exception
    {
        return this.humans > 0;
    }

    public String getRandomName()
    {
        String name;
        do
        {
            name = this.botNames[this.rand.nextInt(botNames.length)];
        }
        while (this.names.contains(name));
        return name;
    }

    public void dealCards() throws Exception
    {
        Logger.debug("[  GameRoom   ] Dealing the cards.");
        this.deck = new Deck();
        this.deck.shuffle();

        for (Map.Entry<String, ActorRef> entry : this.players.entrySet())
        {
            String id = entry.getKey();
            ActorRef player = entry.getValue();
            String name = this.uuids.get(id);

            player.tell(new YourCards(this.deck.giveCards(5)), this.getSelf());

            for (Map.Entry<String, ActorRef> entry2 : this.players.entrySet())
            {
                String id2 = entry2.getKey();
                ActorRef player2 = entry2.getValue();
                if (!id.equals(id2))
                {
                    player2.tell(new OpponentCards(name), this.getSelf());
                }
            }
        }
    }

    public void tellTurn()
    {
        String turnName = this.uuids.get(this.turn.current());
        for (Map.Entry<String, ActorRef> entry : this.players.entrySet())
        {
            String id = entry.getKey();
            ActorRef player = entry.getValue();
            String name = this.uuids.get(id);

            if (this.turn.myTurn(id))
            {
                Logger.debug("[  GameRoom   ] " + name + " - your turn.");
                player.tell(
                        new ItsYourTurn(this.state.current(), this.bets
                                .canCheck(), this.bets.canBet(), this.bets
                                .canRaise(this.turn.current()), this.bets
                                .canCall(this.turn.current()), this.bets
                                .canAllIn(this.turn.current()),
                                this.bets.maxBet, this.bets.getMoney(this.turn
                                        .current()), this.bets.getBet(this.turn
                                        .current()), this.bets.getPool()), this
                                .getSelf());
                if (this.state.current().equals(GameState.EXCHANGING))
                {
                    player.tell(new ConsoleMsg("success",
                            "It's your turn to exchange cards."), this
                            .getSelf());
                }
            }
            else
            {
                Logger.debug("[  GameRoom   ] " + name + " - opponent turn ("
                        + turnName + ").");
                player.tell(new OpponentTurn(turnName), this.getSelf());
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void revealCards() throws Exception
    {
        for (Map.Entry<String, ActorRef> entry : this.players.entrySet())
        {
            String id = entry.getKey();
            ActorRef player = entry.getValue();
            String name = this.uuids.get(id);
            List<Card> hand = (List<Card>) Await.result(
                    ask(player, new TellYourCards(), 1000),
                    Duration.create(5, SECONDS));
            for (Map.Entry<String, ActorRef> entry2 : this.players.entrySet())
            {
                ActorRef opponent = entry2.getValue();
                opponent.tell(new RevealCards(name, hand), this.getSelf());
            }
        }
    }

    public void takeFee()
    {
        for (Map.Entry<String, ActorRef> entry : this.players.entrySet())
        {
            String id = entry.getKey();
            ActorRef player = entry.getValue();
            String name = this.uuids.get(id);
            if ((this.bets.getMoney(id) <= this.initFee)
                    || name.equals("Jenny"))
            {
                this.consoleAll("danger", name
                        + " does not have money to continue.");
                this.getSelf().tell(new Quit(id), this.getSelf());
            }
            else
            {
                this.bets.putFee(id, this.initFee);
                this.tellAll(new NewResults(name, this.bets.getMoney(id),
                        this.results.get(id)));
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void evaluateHands() throws Exception
    {
        Evaluator eval = new Evaluator();
        for (Map.Entry<String, ActorRef> entry : this.players.entrySet())
        {
            String id = entry.getKey();
            ActorRef player = entry.getValue();
            List<Card> hand = (List<Card>) Await.result(
                    ask(player, new TellYourCards(), 1000),
                    Duration.create(5, SECONDS));
            if (!this.turn.folders.contains(id))
            {
                eval.addHand(id, hand);
            }
        }
        Winner winner = eval.getWinner();
        if (winner == null)
        {
            this.tellAll(new ConsoleMsg("warning", "Math ended with draw."));
        }
        else
        {
            int winnerPoints = this.results.get(winner.uuid);
            this.results.remove(winner.uuid);
            this.results.put(winner.uuid, winnerPoints + 1);
            String winnerName = this.uuids.get(winner.uuid);
            int winCash = this.bets.countWin(winner.uuid);
            this.bets.addCash(winner.uuid, winCash);
            this.tellAll(new ConsoleMsg("warning", winnerName
                    + " has win this game with " + winner.eval.toString() + "."));
            this.tellAll(new NewResults(winnerName, this.bets
                    .getMoney(winner.uuid), this.results.get(winner.uuid)));
        }
        this.bets.resetAuction();
        Map<String, Evaluation> evals = eval.getEvaluations();
        Map<String, String> evalNames = new HashMap<String, String>();
        for (Map.Entry<String, Evaluation> entry : evals.entrySet())
        {
            String id = entry.getKey();
            Evaluation e = entry.getValue();
            String playerName = this.uuids.get(id);
            String evalName = e.toString();
            evalNames.put(playerName, evalName);
        }
        this.tellAll(new EvaluationsNames(evalNames));
    }

    public void pauseGame()
    {
        Logger.debug("[  GameRoom   ] Pausing the game.");
        this.gameStarted = false;
    }

    @Override
    public void preStart()
    {
        Logger.debug("[  GameRoom   ] Spawned  game room.");
    }

    @Override
    public void postStop()
    {
        Logger.debug("[  GameRoom   ] Killed game room.");
    }

    public int freeSits() throws Exception
    {
        return this.maxPlayers - this.players.size();
    }

}
